package git.soulbgm.config;

import git.soulbgm.common.annotation.RemoteProcedureCall;
import git.soulbgm.utils.StringUtil;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 贺瑞杰
 * @version V1.0
 * @date 2019-04-02 17:44
 * @description
 */
public class RemoteMethodScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

    private static final char PACKAGE_SEPARATOR = '.';

    private static final char PATH_SEPARATOR = '/';

    private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    private static final String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(RemoteProcedureCall.class.getName()));
        String[] basePackages = annoAttrs.getStringArray("basePackages");
        List<Class<?>> classList = new ArrayList<>();
        for (String basePackage : basePackages) {
            String scanPackagePath = basePackage.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
            classList.addAll(scanPackage(scanPackagePath));
        }
        for (Class<?> cls : classList) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(cls);
            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
            definition.getPropertyValues().add("interfaceClass", definition.getBeanClassName());
            definition.setBeanClass(InterfaceFactoryBean.class);
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            String simpleName = cls.getSimpleName();
            simpleName = StringUtil.toLowerCase(simpleName, 0, 1);
            registry.registerBeanDefinition(simpleName, definition);
        }
    }

    private List<Class<?>> scanPackage(String scanPackagePath) {
        ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(resourceLoader);
        Resource[] resources = new Resource[0];
        scanPackagePath = CLASSPATH_ALL_URL_PREFIX + scanPackagePath + "/" + DEFAULT_RESOURCE_PATTERN;
        try {
            resources = resolver.getResources(scanPackagePath);
        } catch (IOException e) {
            e.printStackTrace();
        }

        List<Class<?>> classList = new ArrayList<>();
        try {
            for (Resource r : resources) {
                MetadataReader reader = metaReader.getMetadataReader(r);
                String className = reader.getClassMetadata().getClassName();
                Class cls = Class.forName(className);
                classList.add(cls);
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return classList;
    }

    private ResourceLoader resourceLoader;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

}
